// Gtk+ icon view based on the Mission icon cell renderer
// Copyright 2006 (C) Ralph Thomas

#include <view/icon.h>
#include <view/gtk/common.h>
#include <view/gtk/flyweight/cell-renderer-icon.h>
#include <binder.hpp>
#include <gtk/os_utilities.hpp>

namespace view {
    struct icon_t {
		typedef adobe::implementation::signal_wrapper_one_t<gboolean,GdkEventExpose*>	paint_handler_t;
		adobe::auto_ptr<GtkWidget>  		control_m;
		GtkCellRenderer*        		    renderer_m;
		paint_handler_t         		    on_paint_m;
		adobe::binder_helper_t				datapump_m;
		GdkRectangle						rectangle_m;
		boost::signals::scoped_connection	connection_m;

		gboolean paint(GdkEventExpose* expose) {
			if (!renderer_m) return false;
			rectangle_m.x = 0; rectangle_m.y = 0;
			gdk_window_clear_area(control_m.get()->window, rectangle_m.x, rectangle_m.y, rectangle_m.width, rectangle_m.height);
			gtk_cell_renderer_render(renderer_m, control_m.get()->window, control_m.get(), &rectangle_m, &rectangle_m, &rectangle_m, (GtkCellRendererState)0);
			return true;
		}
		void update(const adobe::any_regular_t& value) {
			std::string url(value.cast<std::string>());
			GValue val = { 0 };
			g_value_init(&val, G_TYPE_STRING);
			g_value_set_string(&val, value.cast<std::string>().c_str());
			g_object_set_property(G_OBJECT(renderer_m), "icon-name", &val);
			g_value_unset(&val);
			gtk_widget_queue_draw(control_m.get());
		}
		icon_t(unsigned int size, const adobe::any_regular_t& bind, adobe::sheet_t& sheet) : datapump_m( sheet ) {
			rectangle_m.width = rectangle_m.height = size;
			control_m.reset(gtk_event_box_new());
//			gtk_event_box_set_visible_window(GTK_EVENT_BOX(control_m.get()), false);
			on_paint_m.set_callback(boost::bind( &icon_t::paint, this, _1 ));
			on_paint_m.connect(control_m.get(), "expose-event", true);
			renderer_m = mission_cell_renderer_icon_new();
			g_object_set(renderer_m, "size", size, NULL);
			mission_cell_renderer_icon_set_notify_function(renderer_m, boost::bind( &icon_t::redraw_index, this, _1 ));
			connection_m = mission_cell_renderer_icon_inval(boost::bind( &icon_t::redraw, this ));
			datapump_m.initialize(bind, boost::bind( &icon_t::update, this, _1 ), 0);	
		}
		~icon_t() {
			g_object_unref(renderer_m);
		}
		void redraw() { gtk_widget_queue_draw(control_m.get()); }
		void redraw_index(unsigned int) { redraw(); }
		void place(const adobe::place_data_t& extents) {
			adobe::implementation::set_bounds_widget( control_m.get(), extents );
		}
		void measure(adobe::extents_t& extents) { }
	};
	adobe::widget_node_t icon_factory(
		const adobe::dictionary_t& 	parameters,
		const adobe::widget_node_t&	parent,
		const adobe::factory_token_t&	token )
	{
		//
		// It's invalid to not have a "bind" parameter.
		//
		adobe::any_regular_t bind = parameters.at( adobe::static_name_t( "url" ) );
		unsigned int size = 64;
		parameters.get<unsigned int>( adobe::static_name_t( "size" ), size );
		//
		// Create an icon_t.
		//
		icon_t* icon( new icon_t( size, bind, token.sheet_m ) );
		token.client_holder_m.assemblage_m.delete_on_destruction( icon );
		//
		// Insert this widget into it's parent.
		//
		GtkWidget* tmp(icon->control_m.get());
		token.display_m.insert( parent.display_token_m, tmp );
		//
		// Bind the calculate and place signals for Eve.
		//
		adobe::layout_attributes_t attributes;
		attributes.width() = size;
		attributes.height() = size;
		adobe::any_placeable_t* p(new adobe::any_placeable_t(boost::ref(*icon)));
		token.client_holder_m.assemblage_m.delete_on_destruction(p);
		//
		// Register the widget with Eve.
		//
		adobe::eve_t::iterator eve_token(
			token.client_holder_m.eve_m.add_view_element( parent.eve_token_m,
				attributes,
				false,
				parameters,
				boost::ref(*p) )
			);
		//
		// Return the new icon view.
		//
		return adobe::widget_node_t( parent.size_m, eve_token, tmp, parent.keyboard_token_m );
	}
};
